<template>
  <div class="video-manage">
    <div class="search-bar">
      <a-input-search
        v-model:value="queryParams.title"
        placeholder="搜索视频标题"
        enter-button
        @search="handleSearch"
        style="width: 200px"
      />
    </div>

    <div
      v-if="videoList.length > 0"
      class="video-item-list"
    >
      <div
        class="video-list-item"
        v-for="item in videoList"
        :key="item.id"
      >
        <div class="video-container">
          <img
            v-if="item.isVisible == 1 && item.status == '2'"
            :src="item.thumbnailUrl"
            alt="视频缩略图"
            @click="locateToVideo(item.id)"
          />

          <div
            class="video-unseen"
            v-else
          >
            <span v-if="item.status == '0'">转码中</span>
            <span v-else-if="item.status == '1'">审核中</span>
            <span v-else-if="item.status == '3'">审核不通过</span>
            <span v-else-if="item.status == '4'">视频违规，已被封禁</span>
            <span v-else>视频已隐藏</span>
          </div>

          <div class="video-info-container">
            <span class="play-count"
              ><PlayCircleOutlined /> {{ formatNumber(item.viewCount) }}</span
            >
          </div>
        </div>

        <div class="video-operation">
          <a-button
            type="link"
            class="video-title"
            :title="item.title"
            @click="locateToVideo(item.id)"
          >
            <strong>{{ item.title }}</strong>
          </a-button>

          <a-dropdown placement="bottom">
            <a
              class="ant-dropdown-link"
              @click.prevent
              style="margin-top: 2px"
            >
              <a-button style="height: 30px">操作</a-button>
            </a>
            <template #overlay>
              <a-menu>
                <a-menu-item v-if="item.status != '3' && item.status != '4'">
                  <a-button
                    type="link"
                    @click="handleDialogOpen(item.id)"
                  >
                    编辑
                  </a-button>
                </a-menu-item>

                <a-menu-item>
                  <a-button
                    type="link"
                    danger
                    @click="handleDelete(item.id)"
                  >
                    删除
                  </a-button>
                </a-menu-item>
              </a-menu>
            </template>
          </a-dropdown>
        </div>

        <span class="video-date">
          {{ dateFormat(item.publishTime) }}
        </span>
      </div>

      <Pagination
        style="margin-top: 20px"
        v-model:page="queryParams.pageNo"
        v-model:limit="queryParams.pageSize"
        :total="total"
        @pagination="getVideoList"
      />
    </div>

    <div
      style="width: 100%"
      v-else
    >
      <a-empty />
    </div>
  </div>

  <teleport to="body">
    <a-modal
      v-model:visible="dialogVisible"
      title="编辑视频"
      @ok="handleSubmit"
      @cancel="handleDialogClose"
      okText="保存"
      cancelText="取消"
      width="50%"
      :zIndex="1050"
      :centered="true"
      :keyboard="false"
      :mask-closable="false"
      :body-style="{ padding: '20px' }"
    >
      <a-form
        ref="formRef"
        :model="form"
        :rules="rules"
        :label-col="{ span: 4 }"
      >
        <a-form-item
          label="视频封面"
          name="thumbnailUrl"
        >
          <a-upload
            list-type="picture-card"
            class="upload-thumbnail"
            :multiple="false"
            :show-upload-list="false"
            :before-upload="handleBeforeThumbnailUpload"
            :file-list="thumbnailList"
            :max-count="1"
            @change="handleThumbnailUpload"
          >
            <div v-if="!form.thumbnailUrl">
              <p class="upload-thumbnail-icon">
                <UploadOutlined />
              </p>
            </div>

            <template v-else>
              <img
                :src="form.thumbnailUrl"
                alt="视频封面"
                style="width: 100%; height: 100%"
              />
            </template>
          </a-upload>
        </a-form-item>

        <a-form-item
          label="视频标题"
          name="title"
        >
          <a-input
            v-model:value="form.title"
            placeholder="请输入视频标题"
            maxlength="25"
            show-count
          />
        </a-form-item>

        <a-form-item
          label="视频简介"
          name="description"
        >
          <a-textarea
            v-model:value="form.description"
            placeholder="请输入视频简介"
            :auto-size="{ minRows: 3, maxRows: 5 }"
            maxlength="500"
            show-count
          />
        </a-form-item>

        <a-form-item
          label="视频类型"
          name="type"
        >
          <a-select
            v-model:value="form.type"
            placeholder="请选择视频类型"
            :options="videoTypeOptions"
          />
        </a-form-item>

        <a-form-item
          label="视频标签"
          name="labelList"
        >
          <a-select
            mode="tags"
            placeholder="请输入视频标签(最多不超过 10 个)"
            :max-tag-count="5"
            :max-tag-text-length="10"
            allow-clear
            :options="[]"
            v-model:value="form.labelList"
            @change="handleTagsChange"
          />
        </a-form-item>

        <!-- 滑块 -->
        <a-form-item
          label="视频可见性"
          name="isVisible"
          required
        >
          <a-switch
            v-model:checked="form.isVisible"
            un-checked-children="隐藏"
            :un-checked-value="0"
            checked-children="可见"
            :checked-value="1"
          />
        </a-form-item>
      </a-form>
    </a-modal>
  </teleport>
</template>

<script setup lang="ts">
import type { CvVideoReqVO, CvVideoSaveReqVO } from '@/types/video'
import { getDictList } from '@/api/dict'
import { deleteVideo, getVideoOperationDetail, getVideoPage, saveVideo } from '@/api/video'
import type { CvVideoItemRespVO } from '@/types/global'
import dayjs from 'dayjs'
import { message, Modal, UploadChangeParam, UploadProps } from 'ant-design-vue'
import { PlayCircleOutlined, UploadOutlined } from '@ant-design/icons-vue'
import { useUserStore } from '@/store/modules/user'
import { Upload } from 'tus-js-client'
import { checkFileExist } from '@/api/file'
import { sha256 } from 'js-sha256'
import { getAccessToken } from '@/utils/auth'

const userStore = useUserStore()
const uploadUrl = import.meta.env.VITE_UPLOAD_URL

const queryParams = ref<CvVideoReqVO>({
  title: '',
  pageNo: 1,
  pageSize: 10
})

const videoTypeOptions = ref<any>([])

const videoList = ref<CvVideoItemRespVO[]>([])
const total = ref(0)

onMounted(async () => {
  videoTypeOptions.value = await getDictList('cv_video_types')
  await getVideoList()
})

const getVideoList = async () => {
  const res = await getVideoPage(queryParams.value)
  videoList.value = res.list
  total.value = res.total
}

/**
 * 日期格式化
 */
const dateFormat = (date: string) => {
  if (!date) {
    return ''
  }

  return dayjs(date).format('YYYY-MM-DD')
}

const dialogVisible = ref(false)
const videoDetail = ref<CvVideoSaveReqVO>()

const formRef = ref()
const form = reactive({
  id: '',
  title: '',
  description: '',
  type: '',
  thumbnail: '',
  thumbnailUrl: '',
  labelList: [],
  isVisible: 1
})

const rules = {
  thumbnailUrl: [
    {
      required: true,
      message: '请上传视频封面',
      trigger: 'change'
    }
  ],
  title: [
    {
      required: true,
      message: '请输入视频标题',
      trigger: 'blur'
    }
  ],
  type: [
    {
      required: true,
      message: '请选择视频类型',
      trigger: 'change'
    }
  ]
}

/**
 * 保存事件
 */
const handleSubmit = () => {
  if (!formRef.value) {
    return
  }

  if (thumbnailIsUploading.value) {
    message.error('正在上传视频封面，请稍后')
    return
  }

  formRef.value
    .validate()
    .then(async () => {
      await saveVideo(<any>{
        id: videoDetail.value?.id,
        userId: userStore.getUserId,
        type: form.type,
        title: form.title,
        description: form.description,
        thumbnail: form.thumbnail,
        labelList: form.labelList,
        isVisible: form.isVisible,
        status: videoDetail.value?.status
      })
      message.success('保存成功')
      handleDialogClose()
      await getVideoList()
    })
    .catch(error => {})
}

/**
 * 对话框开启事件
 */
const handleDialogOpen = async (videoId: number) => {
  if (videoId) {
    videoDetail.value = await getVideoOperationDetail(videoId)
    form.id = videoDetail.value?.id
    form.title = videoDetail.value?.title
    form.description = videoDetail.value?.description
    form.type = videoDetail.value?.type
    form.thumbnail = videoDetail.value?.thumbnail
    form.thumbnailUrl = videoDetail.value?.thumbnailUrl
    form.labelList = videoDetail.value?.labelList.map(item => item.labelName)
    form.isVisible = videoDetail.value?.isVisible
    dialogVisible.value = true
  }
}

/**
 * 对话框关闭事件
 */
const handleDialogClose = () => {
  videoDetail.value = undefined
  dialogVisible.value = false
  form.id = ''
  form.title = ''
  form.description = ''
  form.type = ''
  form.thumbnail = ''
  form.thumbnailUrl = ''
  form.labelList = []
  form.isVisible = 1
}

/**
 * 标签变更
 */
const handleTagsChange = (tags: string[]) => {
  // 文本长度
  const maxLength = 20
  // 限制标签的数量
  const maxTags = 10

  // 限制每个标签的长度
  if (tags.some(tag => tag.length > maxLength)) {
    message.error('标签长度不能超过 10 个字符')
    form.labelList = tags.map(tag => tag.slice(0, maxLength))
  }

  if (tags.length > maxTags) {
    message.error('标签数量超出限制')
    form.labelList = tags.slice(0, maxTags)
  }
}

/**
 * 删除事件
 */
const handleDelete = (videoId: number) => {
  Modal.confirm({
    title: '确定要删除该视频吗？',
    content: '删除后无法恢复',
    okText: '确定',
    cancelText: '取消',
    centered: true,
    onOk: async () => {
      await deleteVideo(videoId)
      message.success('删除成功')
      queryParams.value.pageNo = 1
      await getVideoList()
    }
  })
}

/**
 * 搜索事件
 */
const handleSearch = () => {
  queryParams.value.pageNo = 1
  getVideoList()
}

// ==================== 视频封面上传 ====================

const thumbnailList = ref([])
const thumbnailIsUploading = ref(false)

/**
 * 视频封面上传前校验
 */
const handleBeforeThumbnailUpload = (file: UploadProps['fileList'][number]) => {
  const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'

  if (!isJpgOrPng) {
    message.error('图片格式仅支持：jpg 和 png')
    return false
  }

  const isLt50M = file.size / 1024 / 1024 < 100
  if (!isLt50M) {
    message.error('图片大小不超过 100MB')
    return false
  }

  return isJpgOrPng && isLt50M
}

/**
 * 视频封面上传
 */
const handleThumbnailUpload = async (info: UploadChangeParam) => {
  if (thumbnailIsUploading.value) {
    message.error('正在上传视频封面，请稍后')
    return
  }

  const file = info.file
  if (!file || !file.originFileObj) {
    return
  }

  thumbnailIsUploading.value = true
  try {
    const reader = new FileReader()
    reader.readAsArrayBuffer(file.originFileObj)
    reader.onload = async () => {
      if (reader.result) {
        const uint8View = new Uint8Array(reader.result as ArrayBuffer)
        let hash = sha256.create()
        hash.update(uint8View)
        const hex = hash.hex()
        const res = await checkFileExist(hex)
        if (res?.id && res?.path) {
          form.thumbnail = res?.id
          form.thumbnailUrl = res?.path
          message.success('视频封面上传成功')
        } else {
          if (uploadUrl) {
            const upload = new Upload(file.originFileObj, {
              endpoint: uploadUrl,
              metadata: {
                filename: file.name,
                uploadType: 'thumbnail',
                hash: hex
              },
              headers: {
                Authorization: `Bearer ${getAccessToken()}`
              },
              onError: error => {
                console.error('上传失败:', error)
                message.error('上传失败')
              },
              onProgress: (bytesUploaded, bytesTotal) => {
                const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2)
                console.log(`上传进度: ${percentage}% (${bytesUploaded}/${bytesTotal})`)
              },
              onSuccess: async () => {
                const { id, path } = await checkFileExist(hex)
                if (id && path) {
                  form.thumbnail = id
                  form.thumbnailUrl = path
                  message.success('视频封面上传成功')
                } else {
                  message.error('视频封面上传失败，请重新上传')
                  form.thumbnail = ''
                  form.thumbnailUrl = ''
                }
              }
            })

            await upload.start()
          }
        }
      }
    }
    reader.onerror = error => {
      message.error('视频封面上传失败: 计算文件哈希失败')
    }
  } catch (error) {
    console.error('视频封面上传失败:', error)
    message.error('视频封面上传失败')
    form.thumbnail = ''
    form.thumbnailUrl = ''
  } finally {
    thumbnailIsUploading.value = false
    thumbnailList.value = [] // 清空文件列表，避免重复触发 change 事件
  }
}

/**
 * 跳转到视频详情页
 */
const locateToVideo = (videoId: number) => {
  window.open(`/video?videoId=${videoId}`)
}

/**
 * 播放量格式化
 */
const formatNumber = (num: number) => {
  if (!num) {
    return 0
  }

  if (num >= 1000 && num < 10000) {
    return (num / 1000).toFixed(1) + 'K'
  } else if (num >= 10000) {
    return (num / 10000).toFixed(1) + 'W'
  } else {
    return num.toString()
  }
}
</script>

<style scoped>
.video-manage {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.search-bar {
  display: flex;
  justify-content: flex-end;
  width: 100%;
  margin-bottom: 20px;
}

.video-item-list {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
}

.video-list-item {
  width: 18%;
  margin: 10px;
}

.video-list-item img {
  width: 100%;
  border-radius: 8px;
  height: 120px;
  cursor: pointer;
}

.video-container {
  position: relative;
  width: 100%;
  overflow: hidden;
  cursor: pointer;
}

.video-unseen {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 120px;
  color: #666;
  background: #e8e8e8;
  border-radius: 8px;
}

.video-info-container {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  display: flex;
  justify-content: space-between;
  background: rgba(0, 0, 0, 0.6);
  color: white;
  font-size: 14px;
  font-weight: bold;
}

.play-count {
  padding: 5px;
}

.video-operation {
  display: flex;
  justify-content: space-between;
}

.video-title {
  font-size: 15px;
  padding: 0;
  color: #000000;
}

.video-date {
  font-size: 14px;
  color: #666;
  display: block;
  margin-top: 5px;
}
</style>
